package cn.lxh.blog.service.impl;


import cn.lxh.blog.domain.Article;
import cn.lxh.blog.dto.ArticleDto;
import cn.lxh.blog.mapper.ArticleMapper;
import cn.lxh.blog.query.ArticleQuery;
import cn.lxh.blog.service.IArticleService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ArticleServiceImpl业务层类
 */
@Service // 该类交给spring管理
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@CacheConfig(cacheNames = "article")
// 将数据库设置成只读数据库, 此时若要进行写的操作, 会出现错误, 有事务就以事务方式执行, 没有事务就以非事务方式执行
public class ArticleServiceImpl implements IArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private MongoTemplate template; // 注入直接使用MongoDB


    /**
     * 添加文章类型
     *
     * @param article
     */
    @Transactional // 开始事务
    @Override
    public void add(Article article) {
        articleMapper.add(article);
        // 将数据添加到MongoDB
        template.save(article);
    }

    /**
     * 根据id删除文章类型
     *
     * @param id
     */
    @Transactional // 开始事务
    @Override
    public void delete(Long id) {
        articleMapper.delete(id);
        // 创建查询条件
        Query query = new Query(Criteria.where("id").is(id));
        // 从MongoDB数据库查询数据
        Article article = template.findOne(query, Article.class);
        // article不为null就删除
        if (article != null) {
            // 删除MongoDB中的数据
            template.remove(article);
        }
    }

    /**
     * 修改文章类型
     *
     * @param article
     */
    @Transactional // 开始事务
    @Override
    public void update(Article article) {
        // 设置修改时间
        article.setUpdateTime(new Date());
        articleMapper.update(article);
        // 创建查询条件
        Query query = new Query(Criteria.where("id").is(article.getId()));
        // 从MongoDB数据库查询数据
        Article MongoArticle = template.findOne(query, Article.class);
        // 防止MongoArticle为null
        if (MongoArticle != null) {
            // 设置新对象的_id
            article.set_id(MongoArticle.get_id());
            // 修改MongoDB数据库的数据
            template.save(article);
        }
    }

    /**
     * 根据id查询单个文章类型
     *
     * @param id
     * @return
     */
    @Override
    public Article findOne(Long id) {
        return articleMapper.findOne(id);
    }

    /**
     * 查询所有文章类型
     *
     * @return
     */
    @Override
    public List<Article> findAll() {
        return articleMapper.findAll();
    }

    /**
     * 根据id批量删除文章类型
     *
     * @param ids
     */
    @Transactional // 开始事务
    @Override
    public void patchDelete(Long[] ids) {
        articleMapper.patchDelete(ids);
        for (Long id : ids) {
            // 创建查询条件
            Query query = new Query(Criteria.where("id").is(id));
            // 从MongoDB数据库查询数据
            Article article = template.findOne(query, Article.class);
            // article不为null就删除
            if (article != null) {
                // 删除MongoDB中的数据
                template.remove(article);
            }
        }
    }

    /**
     * 分页查询
     *
     * @param articleQuery
     * @return
     */
    @Override
    public PageInfo<Article> queryPage(ArticleQuery articleQuery) {
        // 开启分页, 它只对紧挨着下面的第一个查询所有的sql语句生效(1. 先查询总数量total 2. 根据application.yml中驱动名自动实现分页,如果是mysql就用limit
        // 3. 自动计算limit的第一个值: limit a, b
        PageHelper.startPage(articleQuery.getCurrentPage(), articleQuery.getPageSize());
        // 查询所有
        List<Article> list = articleMapper.queryPage(articleQuery);
        // 返回对象中有: list当前页分页数据和total总条数
        return new PageInfo<>(list);
    }

    /**
     * 查询文章类型的文章数量
     *
     * @return
     */
    @Override
    public List<ArticleDto> countByArticleType() {
        return articleMapper.countByArticleType();
    }

    /**
     * 查询文章创建时间的文章数量
     *
     * @return
     */
    @Override
    public List<ArticleDto> countByArticleCreateTime() {
        return articleMapper.countByArticleCreateTime();
    }

    /**
     * 查询相关文章
     *
     * @param id
     * @return
     */
    @Override
    public List<Article> getRelatedArticles(Long id) {
        return articleMapper.getRelatedArticles(id);
    }

    /**
     * 根据文章类型id查询文章
     *
     * @param id
     * @return
     */
    @Override
    public List<Article> getArticlesByTypeId(Long id) {
        return articleMapper.getArticlesByTypeId(id);
    }

    /**
     * 根据创建时间查询文章集合
     *
     * @param articleCreateTime
     * @return
     */
    @Override
    public List<ArticleDto> getArticleByTime(String articleCreateTime) {
        // 判断articleCreateTime时间是否空或者是前端传来指定的字符串, 那么就是查询所有文章归档信息
        if (articleCreateTime == null || "".equals(articleCreateTime.trim()) || articleCreateTime.equals("No Time")) {
            // 重新给articleCreateTime赋值
            articleCreateTime = ""; // 空就表示数据库查询没这个条件
        }
        // 根据时间查询文章集合
        List<Article> articleList = articleMapper.getArticleByTime(articleCreateTime);
        // 以文章创建时间的年份分组, String保存的是年份, List<Article>保存当前年份的集合
        // 通过流操作处理list嵌套map类型的数据并对其进行分组, 首先用CollectorBy.GroupBy方法进行分组, 将list拆分为HashMap的分组形式
        // 但HashMap是无序的, 所以需要转为LinkedHashMap的形式
        // LinkedHashMap是HashMap的子类, 是双向链表形式的HashMap,保证了有序性
        Map<String, List<Article>> articleMap = articleList.stream().collect(Collectors.groupingBy(Article::getYear));
        // 获取articleMap的key
        Set<String> years = articleMap.keySet();
        // 创建一个list集合用于数据返回
        List<ArticleDto> list = new ArrayList<>();
        // 遍历years集合, 根据key获取value
        for (String year : years) {
            // 创建一个ArticleDto对象
            ArticleDto articleDto = new ArticleDto();
            // 设置年份
            articleDto.setYear(year);
            // 向文章集合添加数据
            articleDto.setArticleList(articleMap.get(year));
            // 向list集合添加数据, 保存的是文章创建年份和对应的文章集合对象
            list.add(articleDto);
        }
        return list;
    }

    /**
     * 根据文章排行获取文章数据
     * @return
     */
    @Override
    public List<ArticleDto> articleRanking() {
        return articleMapper.articleRanking();
    }

    /**
     * 获取文章数量最多的文章类型
     * @return
     */
    @Override
    public List<ArticleDto> articleTypeRanking() {
        return articleMapper.articleTypeRanking();
    }

    /**
     * 阅读量加1
     * @param articleId
     */
    @Override
    public void addPageView(Long articleId) {
        articleMapper.addPageView(articleId);
    }

}
